using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Security;
using Akcedo.Framework.Infrastructure.ASPNet;
using FwkLight.CodeExtensions;
using FwkLight.Persistence;
using FwkLight.Web;
using StructureMap;

namespace FwkLight.Application
{
  public delegate void TaskExecutionEventHandler ( IList<ValidationData> validationData );

  public abstract class BaseApplicationTask<TEntityType> : BaseApplicationTaskBase, IApplicationTask<TEntityType>
  {
    private readonly INHUnitOfWorkProvider _uowProvider;
    private readonly INHUnitOfWork _uow;
    private readonly List<ValidationData> _taskRequirementMessages = new List<ValidationData>();

    protected bool TaskRequirementsAreSatisfied { get; private set; }

    protected ISessionItemDictionary SessionItemDictionary = ObjectFactory.GetInstance<ISessionItemDictionary>();
    protected IRequestItemDictionary RequestItemDictionary = ObjectFactory.GetInstance<IRequestItemDictionary>();
    protected ICacheItemDictionary CacheItemDictionary = ObjectFactory.GetInstance<ICacheItemDictionary>();

    protected bool KeepsEntityForCurrentRequest
    {
      get
      {
        MemberInfo membInfo = GetType();
        object[] entityRequestPersist = membInfo.GetCustomAttributes(typeof(KeepEntityForCurrentRequestAttribute), true);

        return entityRequestPersist.GetLength(0) != 0;
      }
    }

    protected bool KeepsEntityInCache
    {
      get
      {
        MemberInfo membInfo = GetType();
        object[] entityRequestPersist = membInfo.GetCustomAttributes(typeof(KeepEntityInCacheAttribute), true);

        return entityRequestPersist.GetLength(0) != 0;
      }
    }

    protected bool KeepsEntityInCacheBySetting
    {
      get
      {
        MemberInfo membInfo = GetType();
        object[] entityRequestPersist = membInfo.GetCustomAttributes(typeof(KeepEntityInCacheBySettingAttribute), true);
        
        return entityRequestPersist.GetLength(0) != 0 && PropertiesManager.UseCache;
      }
    }

    protected bool KeepsEntityInCacheShort
    {
      get
      {
        MemberInfo membInfo = GetType();
        object[] entityRequestPersist = membInfo.GetCustomAttributes(typeof(KeepEntityInCacheShortAttribute), true);

        return entityRequestPersist.GetLength(0) != 0;
      }
    }

    public virtual string CacheVariation
    {
      get
      {
        return "";
      }
    }

    //public virtual string CacheResetVariation
    //{
    //  get
    //  {
    //    return "";
    //  }
    //}

    public string CacheKey
    {
      get
      {
        return ApplicationTask.CacheKey(ClassName, CacheVariation);
      }
    }

    private string ClassName
    {
      get
      {
        return GetType().Name;
      }
    }

    private NameValueCollectionItemDictionary EntityInRequestList
    {
      get
      {
        NameValueCollectionItemDictionary dict =
          RequestItemDictionary.Get(ApplicationConstants.Request.EntityInRequestList);

        if (dict == null)
          return new NameValueCollectionItemDictionary();

        return dict;
      }
      set
      {
        RequestItemDictionary.Add(ApplicationConstants.Request.EntityInRequestList, value);
      }
    }

    private TEntityType _entity;
    private bool _cancelAllChecks;
    private bool _hasTaskErrors;

    protected TEntityType Entity
    {
      [DebuggerNonUserCodeAttribute]
      get
      {
        if (KeepsEntityForCurrentRequest)
        {
          var dict = EntityInRequestList;

          var className = ClassName;
          if (dict.Contains(new ItemKey<TEntityType>(className)))
            return dict.Get(new ItemKey<TEntityType>(className));
        }

        if (KeepsEntityInCache || KeepsEntityInCacheShort || KeepsEntityInCacheBySetting)
        {
          if (CacheItemDictionary.Contains(new ItemKey<TEntityType>(CacheKey)))
            return CacheItemDictionary.Get(new ItemKey<TEntityType>(CacheKey));
        }

        return _entity;
      }

      set
      {
        if (KeepsEntityForCurrentRequest)
        {
          var dict = EntityInRequestList;

          var className = ClassName;

          if (dict.Contains(new ItemKey<TEntityType>(className)))
          {
            dict.Remove(new ItemKey<TEntityType>(className));
          }

          if (value != null)
          {
            dict.Add(new ItemKey<TEntityType>(className), value);
            EntityInRequestList = dict;
          }
        }
        else if (KeepsEntityInCache || KeepsEntityInCacheBySetting)
        {
          if (CacheItemDictionary.Contains(new ItemKey<TEntityType>(CacheKey)))
            CacheItemDictionary.Remove(new ItemKey<TEntityType>(CacheKey));
          CacheItemDictionary.Add(new ItemKey<TEntityType>(CacheKey), value);
        }
        else if (KeepsEntityInCacheShort)
        {
          if (CacheItemDictionary.Contains(new ItemKey<TEntityType>(CacheKey)))
            CacheItemDictionary.Remove(new ItemKey<TEntityType>(CacheKey));
          CacheItemDictionary.Add(new ItemKey<TEntityType>(CacheKey), value, 1);
        }
        else
          _entity = value;
      }
    }

    protected INHUnitOfWork UOW
    {
      [DebuggerNonUserCodeAttribute]
      get
      {
        return _uow;
      }
    }

    [DebuggerNonUserCodeAttribute]
    protected BaseApplicationTask ( INHUnitOfWorkProvider uowProvider )
    {
      _uowProvider = uowProvider;

      _uow = _uowProvider.GetCurrentUnitOfWork();
      _uow.Initialize();

    }

    protected virtual void ExecuteInternal ( )
    {
      HookChildExecutionFailedEvents();

      LoadDataNeccesaryForCacheCheckDecisions();

      if (KeepsEntityForCurrentRequest && Entity != null)
        return;

      if (KeepsEntityInCache && CacheItemDictionary.Contains(new ItemKey<TEntityType>(CacheKey)))
        return;

      if (KeepsEntityInCacheShort && CacheItemDictionary.Contains(new ItemKey<TEntityType>(CacheKey)))
        return;

      LoadAdditionalInitialData();

      TaskRequirementsAreSatisfied = true;

      CheckThatInitialRequirementsAreSatisfied();

      if (!SecurityConditionsAreMet())
      {
        throw new SecurityException("You do not have permission for this action.");
      }

      if (TaskRequirementsAreSatisfied)
      {
        ExecuteBusinessLogic();
        OnAfterExecuteBusinessLogic();
      }
      else
      {
        InvokeExecutionFailed(_taskRequirementMessages);
        return;
      }

      // Recheck - child tasks might have thrown an error.
      if (!TaskRequirementsAreSatisfied)
      {
        InvokeExecutionFailed(_taskRequirementMessages);
        return;
      }

      CheckThatFinalRequirementsAreSatisfied();

      if (TaskRequirementsAreSatisfied)
      {
        OnAfterExecuteFinalChecks();

        OnAfterExecuteLogicAndChecks();

        //CacheItemDictionary.UpdateCacheForExecutedTask(this);
        InvokeExecutionSucceed(_taskRequirementMessages);
      }
      else InvokeExecutionFailed(_taskRequirementMessages);
    }

    protected virtual bool SecurityConditionsAreMet()
    {
      return true;
    }

    protected virtual void OnAfterExecuteFinalChecks ( ) { }

    private void HookChildExecutionFailedEvents ( )
    {
      IEnumerable<FieldInfo> fields = from field in GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
                                      where field.FieldType.IsSubclassOfRawGeneric(typeof(BaseApplicationTask<>))
                                      select field;


      foreach (var field in fields)
      {
        BaseApplicationTaskBase task = (BaseApplicationTaskBase)field.GetValue(this);
        if (task == null) continue;
        task.ExecutionFailed += TaskExecutionFailed;
      }
    }

    protected void TaskExecutionFailed ( IEnumerable<ValidationData> validationData )
    {
      _taskRequirementMessages.AddRange(validationData);
      TaskRequirementsAreSatisfied = false;
    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void OnAfterExecuteBusinessLogic ( )
    {

    }

    [DebuggerNonUserCodeAttribute]
    internal virtual void OnAfterExecuteLogicAndChecks ( )
    {

    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void ExecuteBusinessLogic ( )
    {

    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void LoadAdditionalInitialData ( )
    {
    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void LoadDataNeccesaryForCacheCheckDecisions ( )
    {
    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void CheckThatInitialRequirementsAreSatisfied ( )
    {

    }

    [DebuggerNonUserCodeAttribute]
    protected virtual void CheckThatFinalRequirementsAreSatisfied ( )
    {

    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition )
    {
      if (_cancelAllChecks) return;
      if (!condition())
        TaskRequirementsAreSatisfied = false;
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, string message, bool mandatoryCheck )
    {
      if (_cancelAllChecks) return;
      if (!condition())
      {
        if (mandatoryCheck)
          _cancelAllChecks = true;

        TaskRequirementsAreSatisfied = false;
        _taskRequirementMessages.Add(new ValidationData(message));

      }
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, string message, CheckResultType resultType, bool mandatoryCheck )
    {
      if (_cancelAllChecks) return;
      if (!condition())
      {
        if (mandatoryCheck)
          _cancelAllChecks = true;

        TaskRequirementsAreSatisfied = false;
        _taskRequirementMessages.Add(new ValidationData(message, resultType));

      }
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, CheckResultType resultType, bool mandatoryCheck )
    {
      if (_cancelAllChecks) return;
      if (!condition())
      {
        if (mandatoryCheck)
          _cancelAllChecks = true;

        TaskRequirementsAreSatisfied = false;
        _taskRequirementMessages.Add(new ValidationData(resultType));
      }
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, bool mandatoryCheck )
    {
      CheckThat(condition, "", mandatoryCheck);
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, string message )
    {
      CheckThat(condition, message, true);
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, string message, CheckResultType resultType )
    {
      CheckThat(condition, message, resultType, true);
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, CheckResultType resultType )
    {
      CheckThat(condition, resultType, true);
    }

    [DebuggerNonUserCodeAttribute]
    protected void CheckThat ( Func<bool> condition, Func<string> createErrorMessage )
    {
      CheckThat(condition, createErrorMessage, true);
    }

    private void CheckThat ( Func<bool> condition, Func<string> errorMessage, bool mandatoryCheck )
    {
      if (_cancelAllChecks) return;

      if (!condition())
      {
        if (mandatoryCheck)
          _cancelAllChecks = true;

        TaskRequirementsAreSatisfied = false;
        _taskRequirementMessages.Add(new ValidationData(errorMessage()));

      }
    }
  }

  public class CheckResultType
  {

  }

  public class MandatoryCheckFailedException : Exception
  {
  }
}
